<?php
define('LANG_CAL_WEEK', 'week');

/**
 * A Generic Calender Class
 * It Generates an Array of weeks and days as well as other properties of the calendar given month and year
 * 
 * An Example HTML generation is included in function Calendar->getHTML()
 * To get custom HTML, use the Calendar->days property. It is an array of weeks that contains arrays of days
 * 
 * @copyright Creative-Commons 2.0
 * @author gabe@fijiwebdesign.com - Please send me your suggestions
 * @version $Id$
 * 
 * Feel free to use as you wish, if you do show attribution please link to http://www.fijiwebdesign.com/
 * 
 * Parts based on the following works and subject to their terms:
 * http://scripts.franciscocharrua.com/php-calendar.php
 * http://keithdevens.com/software/php_calendar/
 * 
 */
class Calendar {
        
        /**
         * Construct
         * 
         * @param $str_date Any String formatted date understood by strtotime()
         * @link http://www.php.net/manual/en/function.strtotime.php
         */
        function __construct($str_date = 'now') {
                
                $this->time = strtotime($str_date);
                $this->date = getDate($this->time);
                
                // use current month and year if not supplied
                $month = $this->date['mon'];
                $year = $this->date['year'];
                
                // get the dates for the start of this month and next month
                // we don't worry about month=13 as mktime() will take care of this
                $this_month = getDate(mktime(0, 0, 0, $month, 1, $year));
                $next_month = getDate(mktime(0, 0, 0, $month + 1, 1, $year));
                
                // http://scripts.franciscocharrua.com/php-calendar.php
                $this->day = $this_month["mday"];
        $this->month = $this_month["mon"];
        $this->month_name = $this_month["month"];
        $this->year = $this_month["year"];
                $this->first_day_offset = $this_month["wday"];
                $this->days_count = round(($next_month[0] - $this_month[0]) / (60 * 60 * 24));
                $this->days = array();
                
                // fill in days
                $week = 1;
                for($i = 0; $i < $this->days_count+$this->first_day_offset; $i++) {
                        if ($i < $this->first_day_offset) {
                                $this->days[$week][] = null;
                        } else {
                                $this->days[$week][] = $i-$this->first_day_offset+1;
                        }
                        if ($i%7 == 6) {
                                $week++;
                        }
                }
                
                // localized day names
                $this->day_names = $this->_getFullDayNames();
        }
        
        /**
         * Return the week number a day lies in
         * @return Int
         */
        function getWeekNumberByDay($day) {
                $i = $this->first_day_offset+$day;
                return ceil($i/7);
        }
        
        /**
         * Return the week number that today lies in
         * @return Int
         */
        function getCurrentWeekNumber() {
                $day = $this->date['mday'];
                return $this->getWeekNumberByDay($day);
        }
        
        /**
         * Return the Array representing the calendar days
         * @return Array
         */
        function getCalenderMonthDays() {
                return $this->days;
        }
        
        /**
         * Return the first day of the month
         * @return Int
         */
        function getCalenderMonthStart() {
                return $this->days[0];
        }
        
        /**
         * Return the last day of the month
         * @return Int
         */
        function getCalenderMonthEnd($week) {
                return $this->days[count($this->days)-1];
        }
        
        /**
         * Return the Array representing the days in the given week
         * @param Int Week number
         * @return Array
         */
        function getCalenderWeekDays($week) {
                if (isset($this->days[$week])) {
                        return $this->days[$week];
                }
                return false;
        }
        
        /**
         * Return the first day of the given week
         * @param Int Week number
         * @return Int
         */
        function getCalenderWeekStart($week) {
                if (isset($this->days[$week])) {
                        for($i = 0; $i < 7; $i++) {
                                if ($this->days[$week][$i]) {
                                        return $this->days[$week][$i];
                                }
                        }
                }
                return false;
        }
        
        /**
         * Return the last day of the given week
         * @param Int Week number
         * @return Int
         */
        function getCalenderWeekEnd($week) {
                if (isset($this->days[$week])) {
                        for($i = 6; $i >= 0; $i--) {
                                if ($this->days[$week][$i]) {
                                        return $this->days[$week][$i];
                                }
                        }
                }
                return false;
        }
        
        /**
         * Generate a HTML display of the calendar month
         * @return String HTML
         * @param $header Bool[optional] Show the year and month header
         * @param $day_names String[optional] Show the day names
         * @param $day_name_lengh Int[optional] Length of day names
         * @param $callback Function[optional] A callback function to call when each day is rendered to the calendar
         */
        function getMonthHTML($header = true, $day_names = true, $day_name_lengh = 3, $callback = false) {
                $days = $this->getCalenderMonthDays();
                $html[] = '<table class="calendar"><tbody>';
                // print year and month
                if ($header) {
                        $html[] = '<tr class="header">';
                        $html[] = '<th colspan="7" align="center">'.$this->month_name.' '.$this->year.'</th>';
                        $html[] = '</tr>';
                }
                // print the day names
                if ($day_names) {
                        $html[] = '<tr class="day_names">';
                        for($i = 1; $i <= 7; $i++) {
                                $name = $this->getDayName($i, $day_name_lengh);
                                $html[] = '<th class="day_name '.$name.'">'.$name.'</th>';
                        }
                        $html[] = '</tr>';
                }
                // print the calendar days
                foreach($days as $i=>$week) {
                        $html[] = '<tr class="week week'.$i.'">';
                        foreach($week as $j=>$day) {
                                if ($callback != false) {
                                        $day = $callback($day, $i);
                                }
                                $html[] = '<td class="day '.$day_names[$j%7].'" rel="day'.$j.'">'.$day.'</td>';
                        }
                        $html[] = '</tr>';
                }
                $html[] = '<tbody></table>';
                return implode("\n", $html);
        }
        
        /**
         * Generate a HTML display of the calendar week
         * @return String HTML
         * @param $week Int The week number
         * @param $header Bool[optional] Show the year and month header
         * @param $day_names String[optional] Show the day names
         * @param $day_name_lengh Int[optional] Length of day names
         * @param $callback Function[optional] A callback function to call when each day is rendered
         */
        function getWeekHTML($week, $header = true, $day_names = true, $day_name_lengh = 3, $callback = false) {
                $days =& $this->getCalenderWeekDays($week);
                $html[] = '<table class="calendar"><tbody>';
                // print year and month
                if ($header) {
                        $html[] = '<tr class="header">';
                        $html[] = '<th colspan="7" align="center">'.$this->month_name.' '.$this->year.': '.ucfirst(LANG_CAL_WEEK).' '.$week.'</th>';
                        $html[] = '</tr>';
                }
                // print the day names
                if ($day_names) {
                        $html[] = '<tr class="day_names">';
                        for($i = 1; $i <= 7; $i++) {
                                $name = $this->getDayName($i, $day_name_lengh);
                                $html[] = '<th class="day_name '.$name.'">'.$name.'</th>';
                        }
                        $html[] = '</tr>';
                }
                // print the calendar days
                $html[] = '<tr class="week week'.$week.'">';
                foreach($days as $i=>$day) {
                        if ($callback) {
                                $day = $callback($day, $i);
                        }
                        $html[] = '<td class="day '.$day_names[$j%7].'" rel="day'.$j.'">'.$day.'</td>';
                }
                $html[] = '</tr>';
                $html[] = '<tbody></table>';
                return implode("\n", $html);
        }
        
        /**
         * Returns localized full day names
         * @private
         * @author http://keithdevens.com/software/php_calendar/
         * @return Array
         */
        function _getFullDayNames() {
                $day_names = array();
        for($n=1,$t=3*86400; $n<=7; $n++,$t+=86400) {
                $day_names[$n] = ucfirst(gmstrftime('%A',$t));
        }
                return $day_names;
        }
        
        /**
         * Retrieve a Day name given the date
         * @return String
         * @param $date Int Day of month, eg: 3 or 31
         */
        function getDayName($date, $len = null) {
                $i = ($date+$this->first_day_offset)%7;
                $i = $i ? $i : 7; // 7%7 = 0. Which should be 7 as we start count at 1
                
                return $len ? substr($this->day_names[$i], 0, $len) : $this->day_names[$i];
        }
        function element_day($day,$i){
			return $day.'00';
		}
}